package a4ny;

import java.util.ArrayList;
import java.util.Set;
import java.util.TreeMap;

public class Eval {
	private ArrayList<String> words;
	private ArrayList<String> pos;
	private ArrayList<String> ppos;
	private TreeMap<String, TreeMap<String, Integer>> wordAccCounts;
	private TreeMap<String, TreeMap<String, Integer>> pposCounts;
	private TreeMap<String, Integer> posCounts;
	private double[][] confMat;
	private boolean built = false;
	private String[] posArr;
	private String[] wordArr;
	
	public Eval(ArrayList<String> words, ArrayList<String> pos, ArrayList<String> ppos){
		wordAccCounts = new TreeMap<String,TreeMap<String, Integer>>();
		pposCounts = new TreeMap<String,TreeMap<String, Integer>>();
		posCounts = new TreeMap<String, Integer>();
		this.words = words;
		this.pos = pos;
		this.ppos = ppos;
		initialize();
	}
	
	private void initialize(){
		// Count correct/incorrect ppos (used for per word accuracy)
		TreeMap<String, Integer> accCount;
		for(int i = 0; i < words.size(); i++){
			String thisWord = words.get(i);
			if(wordAccCounts.containsKey(thisWord)){
				accCount = wordAccCounts.get(thisWord);
				if(pos.get(i).equals(ppos.get(i))){
					int count = accCount.containsKey("correct") ? accCount.get("correct") : 0;
					accCount.put("correct", count + 1);
					wordAccCounts.put(thisWord, accCount);
				}else{
					int count = accCount.containsKey("incorrect") ? accCount.get("incorrect") : 0;
					accCount.put("incorrect", count + 1);
					wordAccCounts.put(thisWord, accCount);
				}				
			}else{
				accCount = new TreeMap<String, Integer>();
				if(pos.get(i).equals(ppos.get(i))){
					accCount.put("correct", 1);
					wordAccCounts.put(thisWord, accCount);
				}else{
					accCount.put("incorrect", 1);
					wordAccCounts.put(thisWord, accCount);
				}
				wordAccCounts.put(thisWord, accCount);
			}
		}
		
		// Count ppos for each pos (nbr of correct/wrong ppos for each pos)
		TreeMap<String, Integer> tagCount;
		for(int i = 0; i < pos.size(); i++){
			if(pposCounts.containsKey(pos.get(i))){
				tagCount = pposCounts.get(pos.get(i));
				int count = tagCount.containsKey(ppos.get(i)) ? tagCount.get(ppos.get(i)) : 0;
				tagCount.put(ppos.get(i), count + 1);
				pposCounts.put(pos.get(i), tagCount);
			}else{
				tagCount = new TreeMap<String, Integer>();
				tagCount.put(ppos.get(i), 1);
				pposCounts.put(pos.get(i), tagCount);
			}
			
			int c = posCounts.containsKey(pos.get(i)) ? posCounts.get(pos.get(i)) : 0;
			posCounts.put(pos.get(i), c + 1);
		}
		
		// Make an array of the pos tags (used for prints of confusion matrix)
		Set<String> posUnique = posCounts.keySet();
		Object[] posTemp = posUnique.toArray();
		posArr = new String[posTemp.length];
		for(int i = 0; i < posArr.length; ++i){
			posArr[i] = (String)posTemp[i];
		}
		
		// Make an array of the words (used for per word accuracy)
		Set<String> wordUnique = wordAccCounts.keySet();
		Object[] wordTemp = wordUnique.toArray();
		wordArr = new String[wordTemp.length];
		for(int i = 0; i < wordArr.length; ++i){
			wordArr[i] = (String)wordTemp[i];
		}
	}
	
	// Print the accuracy of the ppos for a given word
	public String perWordAccuracy(){
		String ret = "";
		for(int i = 0; i < wordArr.length; ++i){
			ret += wordArr[i] + ": " + wordAccuracy(i);
		}
		return ret;
	}
	private double wordAccuracy(int wordIndex){
		String word = wordArr[wordIndex];
		if(wordAccCounts.containsKey(word)){
			TreeMap<String, Integer> accCount = wordAccCounts.get(word);
			int correct = accCount.containsKey("correct") ? accCount.get("correct") : 0;
			int incorrect = accCount.containsKey("incorrect") ? accCount.get("incorrect") : 0;
			return (double)correct/(double)(correct + incorrect);
		}else{
			return -1;
		}
	}
	
	// Calculate the tagger accuracy
	public double pposAccuracy(){
		double correctPpos = 0;
		double wrongPpos = 0;
		for(int i = 0; i < pos.size(); i++){
			if(pos.get(i).equals(ppos.get(i))){
				correctPpos++;
			}else{
				wrongPpos++;
			}			
		}		
		return correctPpos/(correctPpos + wrongPpos);
	}
	
	private void buildConfusionMatrix(){
		Set<String> tags = pposCounts.keySet();
		double[][] confMat = new double[tags.size()][tags.size()];
		int i = 0;
		for(String t : tags){
			TreeMap<String, Integer> ptagCounts = pposCounts.get(t);
			int j = 0;
			for(String pt : tags){
				if(ptagCounts.containsKey(pt)){
					confMat[i][j] = (double)ptagCounts.get(pt)/(double)posCounts.get(t);
				}
				j++;
			}
			i++;
		}
		this.confMat = confMat;
		built = true;
	}
	
//	public void printConfMat(){
//		if(!built) buildConfusionMatrix();
//		Set<String> tagSet = pposCounts.keySet();
//		Object[] tags = tagSet.toArray();
//		for(int i = 0; i < tags.length; i++){
//			System.out.print("\t" + tags[i]);
//		}
//		for(int i = 0; i < tags.length; i++){
//			System.out.print("\n" + tags[i]);
//			for(int j = 0; j < tags.length; j++){
//				System.out.print("\t" + confMat[i][j]);
//			}
//		}
//		System.out.flush();
//	}
	
//	// Print a row of the confusion matrix, i.e. the predictions for a given pos
//	public void printConfPos(String pos){
//		int posIndex = -1;
//		for(int i = 0; i < posArr.length; i++){
//			if(posArr[i] == pos) posIndex = i;
//		}
//		if(posIndex != -1){
//			printConfPos(posIndex);
//		}else{
//			System.out.println(pos + " not encountered.");
//		}
//	}
//	public void printConfPos(int row){
//		if(!built) buildConfusionMatrix();
//		System.out.print("Correct pos = \"" + posArr[row] + "\". Predicted pos = [ ");
//		for(int col = 0; col < posArr.length; col++){
//			System.out.print("\"" + posArr[col] + "\" " + confMat[row][col]);
//			if(col != posArr.length - 1) System.out.print(", ");
//		}
//		System.out.print(" ]");
//		System.out.println();
//	}
}
